<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>面向对象思考与 golang 编程 - 服务计算 - 中山大学</title>
  <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>

<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

<meta name="theme-color" content="#f8f5ec" />
<meta name="msapplication-navbutton-color" content="#f8f5ec">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#f8f5ec">


<meta name="author" content="潘茂林" /><meta name="description" content="面向对象思考与接口抽象-io库分析 {:.no_toc} 目录 {:toc} 抽象，特别是面向接口的抽象，可以使程序变得更加通用。但通常会牺牲程序的性能，并导致程序设计与开发需" /><meta name="keywords" content="中山大学, 数据科学与计算机, 服务计算" />






<meta name="generator" content="Hugo 0.74.3 with theme even" />


<link rel="canonical" href="http://pmlpml.git.io/service-computing/post/oo-thinking-abstract/" />
<link rel="apple-touch-icon" sizes="180x180" href="/service-computing/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/service-computing/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/service-computing/favicon-16x16.png">
<link rel="manifest" href="/service-computing/manifest.json">
<link rel="mask-icon" href="/service-computing/safari-pinned-tab.svg" color="#5bbad5">



<link href="/service-computing/sass/main.min.8c3cbcb0324c2bb4875ceccba4007cbad4b4ac8377f33af9953c3e7684534a50.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.css" integrity="sha256-7TyXnr2YU040zfSP+rEcz29ggW4j56/ujTPwjMzyqFY=" crossorigin="anonymous">


<meta property="og:title" content="面向对象思考与 golang 编程" />
<meta property="og:description" content="面向对象思考与接口抽象-io库分析 {:.no_toc} 目录 {:toc} 抽象，特别是面向接口的抽象，可以使程序变得更加通用。但通常会牺牲程序的性能，并导致程序设计与开发需" />
<meta property="og:type" content="article" />
<meta property="og:url" content="http://pmlpml.git.io/service-computing/post/oo-thinking-abstract/" />

<meta itemprop="name" content="面向对象思考与 golang 编程">
<meta itemprop="description" content="面向对象思考与接口抽象-io库分析 {:.no_toc} 目录 {:toc} 抽象，特别是面向接口的抽象，可以使程序变得更加通用。但通常会牺牲程序的性能，并导致程序设计与开发需">

<meta itemprop="wordCount" content="4294">



<meta itemprop="keywords" content="" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="面向对象思考与 golang 编程"/>
<meta name="twitter:description" content="面向对象思考与接口抽象-io库分析 {:.no_toc} 目录 {:toc} 抽象，特别是面向接口的抽象，可以使程序变得更加通用。但通常会牺牲程序的性能，并导致程序设计与开发需"/>

<!--[if lte IE 9]>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
<![endif]-->

<!--[if lt IE 9]>
  <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->

</head>
<body>
  <div id="mobile-navbar" class="mobile-navbar">
  <div class="mobile-header-logo">
    <a href="/service-computing/" class="logo">服务计算</a>
  </div>
  <div class="mobile-navbar-icon">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>
<nav id="mobile-menu" class="mobile-menu slideout-menu">
  <ul class="mobile-menu-list">
    <a href="/service-computing/post/index-2020">
        <li class="mobile-menu-item">首页</li>
      </a><a href="/service-computing/post/">
        <li class="mobile-menu-item">归档</li>
      </a><a href="/service-computing/tags/">
        <li class="mobile-menu-item">标签</li>
      </a><a href="/service-computing/about/">
        <li class="mobile-menu-item">关于</li>
      </a>
  </ul>
</nav>
  <div class="container" id="mobile-panel">
    <header id="header" class="header">
        <div class="logo-wrapper">
  <a href="/service-computing/" class="logo">服务计算</a>
</div>

<nav class="site-navbar">
  <ul id="menu" class="menu">
    <li class="menu-item">
        <a class="menu-item-link" href="/service-computing/post/index-2020">首页</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/service-computing/post/">归档</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/service-computing/tags/">标签</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/service-computing/about/">关于</a>
      </li>
  </ul>
</nav>
    </header>

    <main id="main" class="main">
      <div class="content-wrapper">
        <div id="content" class="content">
          <article class="post">
    
    <header class="post-header">
      <h1 class="post-title">面向对象思考与 golang 编程</h1>

      <div class="post-meta">
        <span class="post-time"> 0001-01-01 </span>
        
        
      </div>
    </header>

    <div class="post-toc" id="post-toc">
  <h2 class="post-toc-title"></h2>
  <div class="post-toc-content always-active">
    <nav id="TableOfContents">
  <ul>
    <li><a href="#面向对象思考与接口抽象-io库分析">面向对象思考与接口抽象-io库分析</a>
      <ul>
        <li><a href="#1-iogo-源代码与-io流抽象">1、 io.go 源代码与 IO流抽象</a>
          <ul>
            <li><a href="#11-io-包的职责">1.1 io 包的职责</a></li>
            <li><a href="#12-io-操作原语定义与基础接口">1.2 IO 操作原语定义与基础接口</a></li>
            <li><a href="#13-实用-io-处理函数">1.3 实用 IO 处理函数</a></li>
            <li><a href="#14-典型的应用结构">1.4 典型的应用结构</a></li>
          </ul>
        </li>
        <li><a href="#2io-抽象与流处理应用">2、IO 抽象与流处理应用</a>
          <ul>
            <li><a href="#21-装饰模式">2.1 装饰模式</a></li>
            <li><a href="#22-字符流">2.2 字符流</a></li>
            <li><a href="#23-二进制流">2.3 二进制流</a></li>
          </ul>
        </li>
        <li><a href="#3包的组织结构">3、包的组织结构</a>
          <ul>
            <li><a href="#31-版本版权申明与包描述">3.1 版本版权申明与包描述</a></li>
            <li><a href="#32-包中的-go-文件">3.2 包中的 go 文件</a></li>
            <li><a href="#33-常量与变量">3.3 常量与变量</a></li>
            <li><a href="#34-api-设计顺序">3.4 api 设计顺序</a></li>
          </ul>
        </li>
        <li><a href="#4小结">4、小结</a></li>
      </ul>
    </li>
  </ul>
</nav>
  </div>
</div>
    <div class="post-content">
      <h1 id="面向对象思考与接口抽象-io库分析">面向对象思考与接口抽象-io库分析</h1>
<p>{:.no_toc}</p>
<ul>
<li>目录
{:toc}</li>
</ul>
<p>抽象，特别是面向接口的抽象，可以使程序变得更加通用。但通常会牺牲程序的性能，并导致程序设计与开发需要高度的技巧。不合理的抽象会导致软件包内在逻辑变得复杂与难以维护。抽象设计是人们在软件设计过程中积累的，其中最丰富的资产就是 java 类库的设计，几乎所有现代语言程序库设计都会吸收 java 库设计的思想， golang 也不例外。本文通过对 io 库设计的分析，使用一些相关的包，如strings，bytes等，正确处理普通文件、内存、字符、管道等流式应用，以及常用文本和二进制流的读写，理解抽象设计的重要性。最后讲述编写 go 库编程的基本规范。</p>
<blockquote>
<p>本文要点：</p>
<ol>
<li>io 库的设计</li>
<li>装饰模式（Decorator pattern）与 io 流处理</li>
<li>规范的文本流与二进制流的读写</li>
<li>golang 库编程要点</li>
</ol>
<p>前提条件：</p>
<ol>
<li>面向对象设计与编程基本概念</li>
<li>了解 3-5 种面向对象设计模式</li>
<li>了解 golang 接口值的含义</li>
</ol>
</blockquote>
<h2 id="1-iogo-源代码与-io流抽象">1、 io.go 源代码与 IO流抽象</h2>
<p>阅读 <a href="https://godoc.org/io">io 库文档</a> 真不如阅读 <a href="https://godoc.org/io">io.go 源代码</a>，因为源代码是按作者思考逻辑组织的，而参考文档是按固定结构、字典顺序组织的。</p>
<h3 id="11-io-包的职责">1.1 io 包的职责</h3>
<p>包设计的基本原则是职责内聚，包设计职责描述是判断设计合理性的重要方面。它通常可以用几句化描述，就 IO 包而言：</p>
<ul>
<li>提供 io流操作原语（I/O primitives） 的基本接口。这些接口为实现 io 流处理的包，如 os 等包在实现流操作时，提供统一的抽象。</li>
<li>提供一些实用函数，方便各种IO流的处理</li>
<li>除非特别声明，这些原语实现不能假设为线程安全</li>
</ul>
<h3 id="12-io-操作原语定义与基础接口">1.2 IO 操作原语定义与基础接口</h3>
<p>在源代码中，对于 IO 流，定义了四个基本操作原语，分别用 Reader，Writer，Closer，Seeker 接口表达二进制流读、写、关闭、寻址操作。 源代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Reader</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Writer</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">Write</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Closer</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">Close</span><span class="p">()</span> <span class="kt">error</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Seeker</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">Seek</span><span class="p">(</span><span class="nx">offset</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">whence</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="kt">int64</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>然后，定义了一些原语组合，表达一些常用的流文件的处理能力。如只读、只写、可读写流。 源代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// ReadWriter is the interface that groups the basic Read and Write methods.
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">ReadWriter</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nx">Reader</span>
	<span class="nx">Writer</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">ReadCloser</span> <span class="kd">interface</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>
<span class="kd">type</span> <span class="nx">WriteCloser</span> <span class="kd">interface</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>
<span class="kd">type</span> <span class="nx">ReadWriteCloser</span> <span class="kd">interface</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>

<span class="kd">type</span> <span class="nx">ReadSeeker</span> <span class="kd">interface</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>
<span class="kd">type</span> <span class="nx">WriteSeeker</span> <span class="kd">interface</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>
<span class="kd">type</span> <span class="nx">ReadWriteSeeker</span> <span class="kd">interface</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>为了兼容以往流编程习惯，IO 库定义了一些常见的基本操作，例如，读写一个字符，seek 到特定位置读写，字串的读写等，源代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">ReaderAt</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">ReadAt</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">off</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">WriterAt</span> <span class="kd">interface</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>

<span class="kd">type</span> <span class="nx">ByteReader</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">ReadByte</span><span class="p">()</span> <span class="p">(</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">ByteWriter</span> <span class="kd">interface</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>

<span class="kd">type</span> <span class="nx">RuneReader</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">ReadRune</span><span class="p">()</span> <span class="p">(</span><span class="nx">r</span> <span class="kt">rune</span><span class="p">,</span> <span class="nx">size</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// stringWriter is the interface that wraps the WriteString method.
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">stringWriter</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">WriteString</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>这里有一些细节：</p>
<ul>
<li>为社么一些接口命名方法不统一，如：ReadAt？</li>
<li>内部接口 <code>stringWriter</code> 有什么用？</li>
<li>是否需要更多的定义，如 IntReader？</li>
</ul>
<h3 id="13-实用-io-处理函数">1.3 实用 IO 处理函数</h3>
<p>在满足上述原语定义的流，就有许多常见的实用功能。提供这些函数不仅方便用户使用该 IO 库，同时也展现了抽象带来的应用价值。</p>
<p>例如：复制流（文件copy），源代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">Copy</span><span class="p">(</span><span class="nx">dst</span> <span class="nx">Writer</span><span class="p">,</span> <span class="nx">src</span> <span class="nx">Reader</span><span class="p">)</span> <span class="p">(</span><span class="nx">written</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="nf">copyBuffer</span><span class="p">(</span><span class="nx">dst</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// copyBuffer is the actual implementation of Copy and CopyBuffer.
</span><span class="c1">// if buf is nil, one is allocated.
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">copyBuffer</span><span class="p">(</span><span class="nx">dst</span> <span class="nx">Writer</span><span class="p">,</span> <span class="nx">src</span> <span class="nx">Reader</span><span class="p">,</span> <span class="nx">buf</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">written</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="c1">// If the reader has a WriteTo method, use it to do the copy.
</span><span class="c1"></span>	<span class="c1">// Avoids an allocation and a copy.
</span><span class="c1"></span>	<span class="k">if</span> <span class="nx">wt</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">src</span><span class="p">.(</span><span class="nx">WriterTo</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>
		<span class="k">return</span> <span class="nx">wt</span><span class="p">.</span><span class="nf">WriteTo</span><span class="p">(</span><span class="nx">dst</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="c1">// Similarly, if the writer has a ReadFrom method, use it to do the copy.
</span><span class="c1"></span>	<span class="k">if</span> <span class="nx">rt</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">dst</span><span class="p">.(</span><span class="nx">ReaderFrom</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>
		<span class="k">return</span> <span class="nx">rt</span><span class="p">.</span><span class="nf">ReadFrom</span><span class="p">(</span><span class="nx">src</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="k">if</span> <span class="nx">buf</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="nx">size</span> <span class="o">:=</span> <span class="mi">32</span> <span class="o">*</span> <span class="mi">1024</span>
		<span class="k">if</span> <span class="nx">l</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">src</span><span class="p">.(</span><span class="o">*</span><span class="nx">LimitedReader</span><span class="p">);</span> <span class="nx">ok</span> <span class="o">&amp;&amp;</span> <span class="nb">int64</span><span class="p">(</span><span class="nx">size</span><span class="p">)</span> <span class="p">&gt;</span> <span class="nx">l</span><span class="p">.</span><span class="nx">N</span> <span class="p">{</span>
			<span class="k">if</span> <span class="nx">l</span><span class="p">.</span><span class="nx">N</span> <span class="p">&lt;</span> <span class="mi">1</span> <span class="p">{</span>
				<span class="nx">size</span> <span class="p">=</span> <span class="mi">1</span>
			<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
				<span class="nx">size</span> <span class="p">=</span> <span class="nb">int</span><span class="p">(</span><span class="nx">l</span><span class="p">.</span><span class="nx">N</span><span class="p">)</span>
			<span class="p">}</span>
		<span class="p">}</span>
		<span class="nx">buf</span> <span class="p">=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">size</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="nx">nr</span><span class="p">,</span> <span class="nx">er</span> <span class="o">:=</span> <span class="nx">src</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">)</span>
		<span class="k">if</span> <span class="nx">nr</span> <span class="p">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
			<span class="nx">nw</span><span class="p">,</span> <span class="nx">ew</span> <span class="o">:=</span> <span class="nx">dst</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">buf</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="nx">nr</span><span class="p">])</span>
			<span class="k">if</span> <span class="nx">nw</span> <span class="p">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
				<span class="nx">written</span> <span class="o">+=</span> <span class="nb">int64</span><span class="p">(</span><span class="nx">nw</span><span class="p">)</span>
			<span class="p">}</span>
			<span class="k">if</span> <span class="nx">ew</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
				<span class="nx">err</span> <span class="p">=</span> <span class="nx">ew</span>
				<span class="k">break</span>
			<span class="p">}</span>
			<span class="k">if</span> <span class="nx">nr</span> <span class="o">!=</span> <span class="nx">nw</span> <span class="p">{</span>
				<span class="nx">err</span> <span class="p">=</span> <span class="nx">ErrShortWrite</span>
				<span class="k">break</span>
			<span class="p">}</span>
		<span class="p">}</span>
		<span class="k">if</span> <span class="nx">er</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
			<span class="k">if</span> <span class="nx">er</span> <span class="o">!=</span> <span class="nx">EOF</span> <span class="p">{</span>
				<span class="nx">err</span> <span class="p">=</span> <span class="nx">er</span>
			<span class="p">}</span>
			<span class="k">break</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="nx">written</span><span class="p">,</span> <span class="nx">err</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>这段代码表明，默认的 Reader 输出到 Writer 采用 32K 缓冲读写。如果有些设备不支持 Buffer 如 Console Input，如果 scr 实现了 WriteTo，就直接写入 dst。</p>
<h3 id="14-典型的应用结构">1.4 典型的应用结构</h3>
<p>自己阅读 SectionReader， 了解 golang 对象的编程习惯。</p>
<ul>
<li>NewXXXX(&hellip;)</li>
<li>定义 XXX 结构</li>
<li>定义 XXX 的方法</li>
</ul>
<h2 id="2io-抽象与流处理应用">2、IO 抽象与流处理应用</h2>
<p>io 包是基础抽象库，它的目标是支持各种类型的流应用。因此，研究提供不同应用场景流服务的方法远比 分析 <code>io/ioutil</code> 子包有价值。本节参照 java io 库的思路，首先介绍装饰模式（Decorator），也称 Wrapper 在 IO 库设计中的应用，然后探讨 golang 文字流和二进制流处理的相关库。</p>
<h3 id="21-装饰模式">2.1 装饰模式</h3>
<p>装饰模式（Decorator pattern）是 IO 流处理中最典型。例如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">rot13Reader</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">r</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">rr</span> <span class="nx">rot13Reader</span><span class="p">)</span> <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span><span class="o">...</span><span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">sr</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="s">&#34;Lbh penpxrq gur pbqr!&#34;</span><span class="p">)</span>
	<span class="nx">rr</span> <span class="o">:=</span> <span class="nx">rot13Reader</span><span class="p">{</span><span class="nx">sr</span><span class="p">}</span>
	<span class="nx">io</span><span class="p">.</span><span class="nf">Copy</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">rr</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>原来是这个熟悉的程序！正如官网所述</p>
<blockquote>
<p>有种常见的模式是一个 io.Reader 包装另一个 io.Reader，然后通过某种方式修改其数据流。</p>
<p>例如，gzip.NewReader 函数接受一个 io.Reader（已压缩的数据流）并返回一个同样实现了 io.Reader 的 *gzip.Reader（解压后的数据流）。</p>
</blockquote>
<p>类似的，对 Writer 也可以做包装。</p>
<h3 id="22-字符流">2.2 字符流</h3>
<p><strong>strings 包</strong></p>
<p>strings 包提供了 Reader，Builder 类型帮助我们用流的方式高效地读、构建文字流。</p>
<p>例如：将一个字符文件读入内存最方便的方法是 <code>io.Copy(sb, fr)</code>。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="p">(</span>
    <span class="s">&#34;fmt&#34;</span>
    <span class="s">&#34;strings&#34;</span>
    <span class="s">&#34;io&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">sr</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="s">&#34;Hello&#34;</span><span class="p">)</span>
    <span class="nx">sb</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nx">Builder</span><span class="p">{}</span>
    <span class="nx">io</span><span class="p">.</span><span class="nf">Copy</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">sb</span><span class="p">,</span><span class="nx">sr</span><span class="p">)</span>
    <span class="nx">io</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">sb</span><span class="p">,</span><span class="s">&#34;, 世界&#34;</span><span class="p">)</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">sr</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">sb</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><em>注意</em></p>
<ul>
<li>strings.Builder 需要 <strong>golang 1.10 版本</strong>以上，你可能需要升级 go 语言</li>
<li>String 值是不可变的（<strong>Immutable</strong>）。即不能修改部分内容，如 <code>s[i] = '2'</code> 会引发 panic</li>
<li>String 值是 utf-8 格式存储， len 返回是 bytes。 如：<code>s:=&quot;Go编程&quot;; fmt.Println(len(s))</code> 输出为 8；</li>
<li>如果要编辑或处理非 ascii（127内）应将字串转为数组，如 <code>[]byte(s)</code> 或 <code>[]rune(s)</code>
<ul>
<li>千万别以为切片指向原字串，而是指向处理原字串新申请的数组</li>
<li>如果字串很长，转换代价是非常之大，请使用 Reader 是最有效的选择！</li>
</ul>
</li>
<li>io.WriteString 执行的代码是 <code>&amp;sb.WriteString</code> 吗？</li>
</ul>
<p><em>验证</em>:</p>
<ul>
<li><a href="https://www.jianshu.com/p/df92c0ee6cc8">go语言字符串拼接性能分析</a>；<a href="http://herman.asia/efficient-string-concatenation-in-go">原文</a></li>
<li>上述方法，没有使用流实现字串拼接，请测试之。注意：字串长度<strong>可能</strong>影响流的效率
<ul>
<li><a href="https://sourcegraph.com/github.com/golang/go/-/blob/src/strings/builder.go">builder.go</a> 源代码似乎很关注效率</li>
</ul>
</li>
</ul>
<p><em>升级 go</em></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-bash" data-lang="bash">$ sudo yum erase golang
$ wget https://dl.google.com/go/go1.11.linux-amd64.tar.gz
$ sudo tar zxvf go1.11.linux-amd64.tar.gz -C /usr/local
$ vi ~/.profile
<span class="nb">export</span> <span class="nv">GOPATH</span><span class="o">=</span><span class="nv">$HOME</span>/gowork
<span class="nb">export</span> <span class="nv">GOROOT</span><span class="o">=</span>/usr/local/go
<span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:<span class="nv">$GOROOT</span>/bin:<span class="nv">$GOPATH</span>/bin
$ <span class="nb">source</span> ~/.profile
</code></pre></td></tr></table>
</div>
</div><p><strong>fmt 包</strong></p>
<p>有了 Reader 和 Writer 抽象，我们就可以使用 FScan?(&hellip;) Fprint?(&hellip;) 格式化读写文件、内存块、字符串、网络文件等。例如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="s">&#34;fmt&#34;</span>
<span class="kn">import</span> <span class="s">&#34;strings&#34;</span>
<span class="kn">import</span> <span class="s">&#34;io&#34;</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">sr</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="s">&#34;Hello, 世界&#34;</span><span class="p">)</span>
	<span class="nx">sb</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">strings</span><span class="p">.</span><span class="nx">Builder</span><span class="p">{}</span>
	<span class="nx">io</span><span class="p">.</span><span class="nf">Copy</span><span class="p">(</span><span class="nx">sb</span><span class="p">,</span><span class="nx">sr</span><span class="p">)</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">sb</span><span class="p">)</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">sb</span><span class="p">,</span><span class="s">&#34;value is %v!\v&#34;</span><span class="p">,</span> <span class="mi">42</span><span class="p">)</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">sb</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>感觉是否很强大！ 其实 java 1.5 就这样玩了哦。</p>
<p><strong>bytes 包</strong></p>
<p>bytes 包和 strings 包有些类似，有许多函数帮助你处理文字。 如果把 []byte 看作内存块，该包提供的 Reader 和 Buffer。</p>
<ul>
<li>bytes.Reader 实现了  io.Reader, io.ReaderAt, io.WriterTo, io.Seeker, io.ByteScanner, and io.RuneScanner 接口</li>
<li>bytes.Buffer 提供了  io.ReadWriter 等接口。 配合切片指针，用户可以方便读写内存块任意位置。</li>
</ul>
<p>官方给的案例：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">b</span> <span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span> <span class="c1">// A Buffer needs no initialization.
</span><span class="c1"></span><span class="nx">b</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;Hello &#34;</span><span class="p">))</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">b</span><span class="p">,</span> <span class="s">&#34;world!&#34;</span><span class="p">)</span>
<span class="nx">b</span><span class="p">.</span><span class="nf">WriteTo</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p><strong>os.File与bufio包</strong></p>
<p>os.File 实现了文件的读写操作，支持几乎所有 io 原语。 除了用 os.OpenFile 打开文件后，一定要使用 defer 函数 close 文件外，其他 io 操作都和前面类似。</p>
<p>bufio包使用 Wrapper 包装原始的 Reader 和 Writer， 提供 buffered 服务、文字处理、字符流扫描服务：</p>
<ul>
<li>bufio.Reader，提供了原始文件 Reader 不能提供的文字处理的功能， 如： ReadLine， ReadRune，WriteTo 等操作</li>
<li>bufio.Writer, 提供了原始文件 Writer 不能提供的文字处理的功能， 如： ReadFrom， WriteString，WriteRune 等操作</li>
<li>bufio.Scanner，将字符流转为 Token 流
<ul>
<li>请阅读官方案例 <a href="https://godoc.org/bufio#example-Scanner--Words">Example (Words)</a> 和 <a href="https://godoc.org/bufio#example-Scanner--Custom">Example (Custom)</a></li>
<li>请阅读源代码 <a href="https://sourcegraph.com/github.com/golang/go/-/blob/src/bufio/scan.go">scan.go</a></li>
<li>因为解析 XML 等文档都可以用类似的思想处理</li>
</ul>
</li>
</ul>
<p>你可能观察到 bufio.Reader 、bytes.Reader 与 strings.Reader 很多功能相同，如 ReadRune。 这时需要用 bufio.Reader 包装 strings.Reader 吗？</p>
<p><strong>io，io/ioutil 包其他功能</strong></p>
<ul>
<li>必须关注的功能，<a href="https://godoc.org/io#example-Pipe">io.Pipe()</a> 提供了 <strong>线程安全</strong> 的管道服务</li>
<li>可以使用的功能，ioutil.TempFile 和 ioutil.TempDir， 毕竟你不打算在这些小功能上怼 google 工程师</li>
<li>ioutil 其他功能，太鸡肋了。建议你不要使用 io/ioutil 子包，几乎没有存在的价值</li>
</ul>
<h3 id="23-二进制流">2.3 二进制流</h3>
<p>如何将 <code>[]int64{1,2,3,4,5}</code> 或 <code>User{“WuKong”，3800，true}</code> 以二进制方式写入文件或读出？</p>
<p>嗯啊吖，难道就仅支持原始的 <code>[]byte</code> 读写吗？</p>
<p><strong>encoding/binary 包</strong></p>
<p>直接上代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="s">&#34;fmt&#34;</span>
<span class="kn">import</span> <span class="s">&#34;bytes&#34;</span>
<span class="kn">import</span> <span class="s">&#34;encoding/binary&#34;</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">rw</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">{}</span>
	<span class="nx">data</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">int64</span><span class="p">{</span><span class="mi">49</span><span class="p">,</span><span class="mi">50</span><span class="p">,</span><span class="mi">51</span><span class="p">,</span><span class="mi">52</span><span class="p">}</span>
	<span class="nx">binary</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">rw</span><span class="p">,</span><span class="nx">binary</span><span class="p">.</span><span class="nx">LittleEndian</span><span class="p">,</span><span class="nx">data</span><span class="p">)</span> 
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">rw</span><span class="p">,</span> <span class="nx">rw</span><span class="p">.</span><span class="nf">Len</span><span class="p">(),</span> <span class="nx">data</span><span class="p">)</span>
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;% x\n&#34;</span><span class="p">,</span> <span class="nx">rw</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">())</span>
	<span class="nx">size</span> <span class="o">:=</span> <span class="nx">binary</span><span class="p">.</span><span class="nf">Size</span><span class="p">(</span><span class="nx">data</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">size</span><span class="p">)</span>
	<span class="nx">orgin</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int64</span><span class="p">,</span><span class="nx">rw</span><span class="p">.</span><span class="nf">Len</span><span class="p">()</span><span class="o">/</span><span class="nx">size</span><span class="p">)</span>
	<span class="nx">binary</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">rw</span><span class="p">,</span><span class="nx">binary</span><span class="p">.</span><span class="nx">LittleEndian</span><span class="p">,</span><span class="nx">orgin</span><span class="p">)</span> 
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">orgin</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>上述程序按官方说明 <code>Numbers are translated by reading and writing fixed-size values. A fixed-size value is either a fixed-size arithmetic type (bool, int8, uint8, int16, float32, complex64, ...) or an array or struct containing only fixed-size values.</code></p>
<p>这意味结构中包含 string，[]byte 就搞不定了。 是的！ 正确的处理代码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;fmt&#34;</span>
	<span class="s">&#34;bytes&#34;</span>
	<span class="s">&#34;encoding/binary&#34;</span>
<span class="p">)</span>

<span class="kd">type</span> <span class="nx">User</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">name</span> <span class="p">[</span><span class="mi">20</span><span class="p">]</span><span class="kt">byte</span>
	<span class="nx">age</span> <span class="kt">int16</span>
	<span class="nx">sex</span> <span class="kt">bool</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">man</span> <span class="o">:=</span> <span class="nx">User</span><span class="p">{</span><span class="nx">age</span><span class="p">:</span><span class="mi">21</span><span class="p">,</span> <span class="nx">sex</span><span class="p">:</span> <span class="kc">true</span><span class="p">}</span>
	<span class="nx">name</span> <span class="o">:=</span> <span class="nx">man</span><span class="p">.</span><span class="nx">name</span><span class="p">[:]</span>
	<span class="nb">copy</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="s">&#34;Jack&#34;</span><span class="p">)</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">man</span><span class="p">,</span> <span class="nx">binary</span><span class="p">.</span><span class="nf">Size</span><span class="p">(</span><span class="nx">man</span><span class="p">))</span>
	
	<span class="nx">rw</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">{}</span>
	<span class="nx">binary</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">rw</span><span class="p">,</span><span class="nx">binary</span><span class="p">.</span><span class="nx">LittleEndian</span><span class="p">,</span><span class="nx">man</span><span class="p">)</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;% x\n&#34;</span><span class="p">,</span> <span class="nx">rw</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">())</span>
	
	<span class="nx">orgin</span> <span class="o">:=</span> <span class="nx">User</span><span class="p">{}</span>
	<span class="nx">binary</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">rw</span><span class="p">,</span> <span class="nx">binary</span><span class="p">.</span><span class="nx">LittleEndian</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">orgin</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span>
	<span class="nx">binary</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">rw</span><span class="p">,</span> <span class="nx">binary</span><span class="p">.</span><span class="nx">LittleEndian</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">orgin</span><span class="p">.</span><span class="nx">age</span><span class="p">)</span>
	<span class="nx">binary</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">rw</span><span class="p">,</span> <span class="nx">binary</span><span class="p">.</span><span class="nx">LittleEndian</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">orgin</span><span class="p">.</span><span class="nx">sex</span><span class="p">)</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">orgin</span><span class="p">)</span>
	
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello, &#34;</span><span class="p">,</span> <span class="nb">string</span><span class="p">(</span><span class="nx">name</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>你可能有疑问，为什么不使用 <code>binary.Read(rw, binary.LittleEndian, &amp;orgin)</code> ? 出错了， bug！</p>
<p>为社么没改正？因为高手都用 unsafe！</p>
<p><strong>unsafe 包</strong></p>
<p>如果你不考虑 intel CPU 和 一些 RISC CPU 字节序（Endian）不同，二进制文件读写就是 byte数组 与 其他类型 向 c 语言那样转换的问题。golang 提供了 <code>unsafe</code> 包，让你比较方便处理指针。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;fmt&#34;</span>
	<span class="s">&#34;bytes&#34;</span>
	<span class="s">&#34;unsafe&#34;</span>
<span class="p">)</span>

<span class="kd">const</span> <span class="nx">RECORD_LEN</span> <span class="p">=</span> <span class="mi">23</span>

<span class="kd">type</span> <span class="nx">User</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">name</span> <span class="p">[</span><span class="mi">20</span><span class="p">]</span><span class="kt">byte</span>
	<span class="nx">age</span> <span class="kt">int16</span>
	<span class="nx">sex</span> <span class="kt">bool</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">man</span> <span class="o">:=</span> <span class="nx">User</span><span class="p">{</span><span class="nx">age</span><span class="p">:</span><span class="mi">21</span><span class="p">,</span> <span class="nx">sex</span><span class="p">:</span> <span class="kc">true</span><span class="p">}</span>
	<span class="nx">name</span> <span class="o">:=</span> <span class="nx">man</span><span class="p">.</span><span class="nx">name</span><span class="p">[:]</span>
	<span class="nb">copy</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="s">&#34;Jack&#34;</span><span class="p">)</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">man</span><span class="p">)</span>
	
	<span class="nx">rw</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">{}</span>
	<span class="nx">pBytes</span> <span class="o">:=</span> <span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="nx">RECORD_LEN</span><span class="p">]</span><span class="kt">byte</span><span class="p">)(</span><span class="nx">unsafe</span><span class="p">.</span><span class="nf">Pointer</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">man</span><span class="p">))</span>
	<span class="nx">rw</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">pBytes</span><span class="p">[:])</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;% x\n&#34;</span><span class="p">,</span> <span class="nx">rw</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">())</span>
	
	<span class="nx">pUser</span> <span class="o">:=</span> <span class="p">(</span><span class="o">*</span><span class="nx">User</span><span class="p">)(</span><span class="nx">unsafe</span><span class="p">.</span><span class="nf">Pointer</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">rw</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">()[</span><span class="mi">0</span><span class="p">]))</span>
	<span class="nx">orgin</span> <span class="o">:=</span> <span class="o">*</span><span class="nx">pUser</span> <span class="c1">//copy!
</span><span class="c1"></span>	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">orgin</span><span class="p">)</span>
	
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello, &#34;</span><span class="p">,</span> <span class="nb">string</span><span class="p">(</span><span class="nx">name</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>这是处理二进制记录目前最直接的方法，缺点是要事前知道CPU兼容和记录长度。计算这个长度也不容易，你的知道结构的存储方式，即 packed or not！最后注意，理解垃圾回收机制工作模式，控制不安全指针的作用域。</p>
<p><strong>encoding/gob 包</strong></p>
<p>参考官网案例 <a href="https://godoc.org/encoding/gob#pkg-examples">Basic</a></p>
<p>在 encoding 子包中，人们提出基于各种格式的交换数据的方法。如果你只打算 golang 进程之间交换数据， gob 就是较好的选择， 否则就的使用 json， xml等编码和解码方法。</p>
<h2 id="3包的组织结构">3、包的组织结构</h2>
<p>在编程学习中，“依葫芦画瓢”大概是最有效的方法了。阅读完 io 包，你大概知道了写一个包的套路，让我们总结一下：</p>
<h3 id="31-版本版权申明与包描述">3.1 版本版权申明与包描述</h3>
<ul>
<li>写在与包名相同的 go 文件中。 例如 io 包 <a href="https://sourcegraph.com/github.com/golang/go/-/blob/src/io/io.go">io.go</a></li>
<li>写在独立的 doc 文件中。例如 fmt 包 <a href="https://sourcegraph.com/github.com/golang/go/-/blob/src/fmt/doc.go">doc.go</a></li>
</ul>
<h3 id="32-包中的-go-文件">3.2 包中的 go 文件</h3>
<ul>
<li>go 文件一般成对出现，例如：<code>io.go</code> 与 <code>io_test.go</code> 以方便包实现自动化测试</li>
<li>每个 go 文件一定是功能内聚的，如流原语，就围绕流设计 api，而不宜设计文件、管道等</li>
<li>example_test.go 文件， 文件中有 <code>func Example...</code> 函数，用以帮助生成 api 文件</li>
<li>doc.go 复杂 api 的说明文件</li>
</ul>
<h3 id="33-常量与变量">3.3 常量与变量</h3>
<p>go 程序的开始，一定是包用到的常量与变量，复杂的包可以用 <code>vars.go</code> 申明。 定义 errors 是包设计的重要内容之一。 例如 io.go ：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// Seek whence values.
</span><span class="c1"></span><span class="kd">const</span> <span class="p">(</span>
	<span class="nx">SeekStart</span>   <span class="p">=</span> <span class="mi">0</span> <span class="c1">// seek relative to the origin of the file
</span><span class="c1"></span>	<span class="nx">SeekCurrent</span> <span class="p">=</span> <span class="mi">1</span> <span class="c1">// seek relative to the current offset
</span><span class="c1"></span>	<span class="nx">SeekEnd</span>     <span class="p">=</span> <span class="mi">2</span> <span class="c1">// seek relative to the end
</span><span class="c1"></span><span class="p">)</span>

<span class="c1">// ErrShortWrite means that a write accepted fewer bytes than requested
</span><span class="c1">// but failed to return an explicit error.
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">ErrShortWrite</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;short write&#34;</span><span class="p">)</span>

<span class="c1">// ErrShortBuffer means that a read required a longer buffer than was provided.
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">ErrShortBuffer</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;short buffer&#34;</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><h3 id="34-api-设计顺序">3.4 api 设计顺序</h3>
<ul>
<li>抽象事物操作原语接口</li>
<li>具体事物结构与方法实现</li>
<li>实用辅助函数</li>
</ul>
<p>编程不是字典顺序的，要以程序猿逻辑组织代码，别于大家阅读！</p>
<h2 id="4小结">4、小结</h2>
<p>从具体到抽象，在从抽象到具体应用，是人类认识世界的基本方法，程序设计也是如此！如果你理解了 io 的 Reader 和 Writer，文字、内存块、文件等的处理就会得心应手，也不需要记忆那么多烦人的 API 了。例如，遇到复杂的 split ， 你就想到 Scanner 的设计， “依葫芦画瓢” 做一个绝不会比一般人编写的 api 差的。</p>
<p>api 原代码阅读的重要性不用多说，这是 go 进阶的唯一方法。</p>

    </div>

    <div class="post-copyright">
  <p class="copyright-item">
    <span class="item-title"></span>
    <span class="item-content">潘茂林</span>
  </p>
  <p class="copyright-item">
    <span class="item-title"></span>
    <span class="item-content">
        0001-01-01
        
    </span>
  </p>
  
  
</div>
<footer class="post-footer">
      
      <nav class="post-nav">
        <a class="prev" href="/service-computing/post/ex-install-cloud/">
            <i class="iconfont icon-left"></i>
            <span class="prev-text nav-default">私有云</span>
            <span class="prev-text nav-mobile"></span>
          </a>
        <a class="next" href="/service-computing/post/oo-thinking/">
            <span class="next-text nav-default">面向对象思考与 golang 编程</span>
            <span class="next-text nav-mobile"></span>
            <i class="iconfont icon-right"></i>
          </a>
      </nav>
    </footer>
  </article>
        </div>
        

  

  

      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="social-links">
      <a href="mailto:panml@mail.sysu.edu.cn" class="iconfont icon-email" title="email"></a>
  <a href="http://pmlpml.git.io/service-computing/index.xml" type="application/rss+xml" class="iconfont icon-rss" title="rss"></a>
</div>

<div class="copyright">
  <span class="power-by">
    %!(EXTRA string=<a class="hexo-link" href="https://gohugo.io">Hugo</a>)
  </span>
  <span class="division">|</span>
  <span class="theme-info">
     - 
    <a class="theme-link" href="https://github.com/olOwOlo/hugo-theme-even">Even</a>
  </span>

  

  <span class="copyright-year">
    &copy; 
    2018 - 
    2020
    <span class="heart">
      <i class="iconfont icon-heart"></i>
    </span>
    <span class="author">潘茂林</span>
  </span>
</div>
    </footer>

    <div class="back-to-top" id="back-to-top">
      <i class="iconfont icon-up"></i>
    </div>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/slideout@1.0.1/dist/slideout.min.js" integrity="sha256-t+zJ/g8/KXIJMjSVQdnibt4dlaDxc9zXr/9oNPeWqdg=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.js" integrity="sha256-XVLffZaxoWfGUEbdzuLi7pwaUJv1cecsQJQqGLe7axY=" crossorigin="anonymous"></script>



<script type="text/javascript" src="/service-computing/js/main.min.d7b7ada643c9c1a983026e177f141f7363b4640d619caf01d8831a6718cd44ea.js"></script>








</body>
</html>
